package com.supermap.sdc2023.guongquan.usersvc;

import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.crypto.impl.RSASSAProvider;
import io.buji.pac4j.realm.Pac4jRealm;
import org.apache.commons.io.IOUtils;
import org.apache.shiro.env.NamedObjectEnvironment;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.env.EnvironmentLoader;
import org.apache.shiro.web.env.EnvironmentLoaderListener;
import org.apache.shiro.web.env.WebEnvironment;
import org.apache.shiro.web.servlet.ShiroFilter;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.pac4j.jwt.config.signature.RSASignatureConfiguration;
import org.pac4j.jwt.credentials.authenticator.JwtAuthenticator;
import org.pac4j.jwt.util.JWKHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import javax.servlet.ServletContext;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.util.ArrayList;

@SpringBootApplication
public class UsersvcApplication {

    public static void main(String[] args) {
        SpringApplication.run(UsersvcApplication.class, args);
    }

    @Configuration
    public static class ShiroConfiguratoin {
        @Value("${jwtcertsUrl}")
        private String certsUrl;

        @Bean(name = {"shiroFilter"})
        public FilterRegistrationBean<? extends Filter> shiroFilter() {
            ShiroFilter shiroFilter = new ShiroFilter();
            FilterRegistrationBean<ShiroFilter> result = new FilterRegistrationBean<>();
            result.setFilter(shiroFilter);
            result.addInitParameter(EnvironmentLoader.CONFIG_LOCATIONS_PARAM, ResourceUtils.CLASSPATH_PREFIX + "shiro.ini");
            result.addUrlPatterns("/*");
            return result;
        }

        public void init(JwtAuthenticator jwtAuthenticator) throws IOException {
            String json = IOUtils.toString(new URL(certsUrl), StandardCharsets.UTF_8);
            JSONObject jsonObject = new JSONObject(new JSONTokener(json));
            JSONArray jsonArrayKeys = jsonObject.getJSONArray("keys");
            jwtAuthenticator.setSignatureConfigurations(new ArrayList<>());
            for (int i = 0; i < jsonArrayKeys.length(); i++) {
                JSONObject keyObj = jsonArrayKeys.getJSONObject(i);
                KeyPair keyPair = JWKHelper.buildRSAKeyPairFromJwk(keyObj.toString());
                RSASignatureConfiguration signatureConfiguration = new RSASignatureConfiguration(keyPair);
                String alg = keyObj.getString("alg");
                JWSAlgorithm algType = JWSAlgorithm.parse(alg);
                if (!RSASSAProvider.SUPPORTED_ALGORITHMS.contains(algType)) {
                    continue;
                }
                signatureConfiguration.setAlgorithm(algType);
                jwtAuthenticator.addSignatureConfiguration(signatureConfiguration);
            }
            jwtAuthenticator.reinit();
        }

        @Bean
        public EnvironmentLoaderListener environmentLoaderListener() {
            return new EnvironmentLoaderListener() {
                @Override
                public WebEnvironment initEnvironment(ServletContext servletContext) throws IllegalStateException {
                    WebEnvironment result = super.initEnvironment(servletContext);
					try {
						afterInit(result);
					} catch (IOException e) {
						throw new RuntimeException(e);
					}
					return result;
                }

                private void afterInit(WebEnvironment environment) throws IOException {
                    NamedObjectEnvironment namedObjectEnvironment = NamedObjectEnvironment.class.cast(environment);
                    JwtAuthenticator jwtAuthenticator = namedObjectEnvironment.getObject("jwtAuthenticator", JwtAuthenticator.class);
                    init(jwtAuthenticator);
                }
            };
        }
    }

    @Configuration
    public static class JaxrsResourceConfiguration {
        @Autowired
        private UserResource userResource;

        @Bean
        public ResourceConfig resourceConfig() {
            return new ResourceConfig().register(userResource)
                    .register(new AbstractBinder() {
                        @Override
                        protected void configure() {
                            bindFactory(SubjectFactory.class).to(Subject.class);
                        }
                    });
        }
    }

}
